定义原始数据类型
null
和undefined
是所有类型的子类型。可以复制给任何类型let arrOfNum: number[] = [1, 2, 3, 4]; // 定义元素类型为数子的数组
tuple
元组类型let user: [string, number] = ['jolly', 30]; // 两项,第一项类型为 string, 第二项类型为 number
函数声明
// 指定参数类型和返回值类型 function add(x: number, y: number, z?: number): number{ return x + y; } let add2 = (x: number, y: number, z?: number): number => { return x + y; } let add3: (x: number, y:number) => number = add2;
Type inference 类型推断
let s = 'str'; // s 类型判定为 'string'. 不能被赋值为非 'string' 类型的值。
interface
- 定义对象类型及形状
interface Person {
readonly id: number,
name: string,
age?: number
}
// 合法代码
let jolly: Person = {
name: 'jolly',
age: 30,
id: 1 // id不能被修改
}
- 定义函数类型
interface Count {
(x: number, y: number): number // 定义参数类型和返回值
}
const sum: Count = (x: number, y: number) => x + y;
- Indexable type 可索引类型
interface Randmap {
[propName: string]: string;
}
const map: Randmap = {
a: 'a',
b: 'b',
c: 'c' // 合法
// d: 1 // 不合法,期望 String
}
- LIke Array 类型
interface LikeArray {
[index: number]: string
}
const likeArray: LikeArray = ['a', 'b', 'c'];
likeArray[0]; // 只能访问,没有其他方法
- duck typing 鸭子类型
// 定义一个四不像
interface FunWithProps {
(x: number): number;
name: string;
}
const a: FunWithProps = (x: number) => { // 传入类型要为 number
// return 'x' // 不合法,返回类型应为 number
return x
}
a.name = 'adf' // 类型要为 string
类和接口
public
private
- 只在实例上可以访问,子类上不能访问
protected
- 只在子类上可以访问,实例上不能访问
- interface 约束类,implements 实现类
interface ClockInterface {
currentTime: number,
alert(): void
}
interface GameInterface {
play(): void
}
// 合法代码
class Cellphone implements ClockInterface, GameInterface {
currentTime: number = 123;
alert() {
}
play() {
}
}
interface 约束构造函数
类的类型由两部分组成:
- 静态类型,这个类本身的类型
- 实例类型,使用
new
关键字创建的实例的类型
构造函数由约束静态类型,从而得到约束
// 约束静态类型
interface ClockStatic {
new (h: number, m: number): void;
time: number
}
// 约束实例类型的属性方法
interface ClockInterface {
currentTime: number,
alert(): void
}
// 约束实例类型的属性方法
interface GameInterface {
play(): void
}
const Cellphone: ClockStatic = class Cellphone implements ClockInterface, GameInterface {
constructor(h: number, m1: number){
}
static time: 12
currentTime: number = 123;
alert() {
}
play() {
}
}
泛型
函数和泛型
泛型解决的问题
类型推断不能延伸到函数
- 泛型是在定义函数和接口的时候,不预先指定类型,而在使用时指定类型的一种特性
function echo<T>(arg: T): T { return arg; // T 是泛型的名称,可随意起名。可以理解为将来由参数类型替代 } const result = echo('str'); // result 类型为 string。T = string // 泛型可以传入多个值 function swap<T, U>(tuple: [T, U]): [U, T] { return [tuple[1], tuple[0]]; } //
泛型和接口
从 react
定义文件学习 泛型和接口
安装 @types/react
使用命令 create-react-app
创建 react
项目,返现并没有 @types/react
于是手动安装
npm i -S @types/react
安装了之后,在 tsx
文件中才能 引入 FunctionComponent
接口,不然会报错 “react 没有导出 FunctionComponent”
用来学习的 ts
源码便是 FunctionComponent
接口的定义
interface FunctionComponent<P = {}> {
(props: PropsWithChildren<P>, context?: any): ReactElement<any, any> | null;
propTypes?: WeakValidationMap<P>;
contextTypes?: ValidationMap<any>;
defaultProps?: Partial<P>;
displayName?: string;
}
例:
import { FunctionComponent } from 'react'
interface TestProps {
title: string,
desc: string
}
// 将接口 TestProps 传递到函数FunctionComponent中
const Test: FunctionComponent<TestProps> = (props) => {
return (
<div>
<h1>{props.title}</h1>
<p>{props.desc}</p>
</div>
)
}
从 react
ts
定义文件源码得知
泛型的默认值
<P = {}>
类型别名:
type
type PlusType = (x: number, y: number) => number let sum: PlusType = (x: numver, y: number) => x + y // WeakValidationMap 的定义 type WeakValidationMap<T> = { [K in keyof T]?: null extends T[K] ? Validator<T[K] | null | undefined> : undefined extends T[K] ? Validator<T[K] | null | undefined> : Validator<T[K]> };
交叉类型 '&'
同时要有两个接口中的定义的数据
interface IName { name: string } type IPerson = IName & { age: number } let person: IPerson = { name: 'hello', age: 12 } // 同时要有两个接口中的定义的数据 // 源码中定义 PropsWithChildren 类型,用到了 type PropsWithChildren<P> = P & { children?: ReactNode }
联合类型 '|' 注意,在
typescript
不确定传入的类型是联合类型中的哪种时,我们只能访问两种类型共有的属性和方法。怎样判断是联合类型中的哪种类型,请看后面的 “类型断言”let numberOrString: number | string // numberOrString 为 number 或 string 类型
Partial
功能,接受一个泛型, 将其中的属性或函数变为可选。是typescript
内置类型interface Person { name: string, age: number } type PersonOptional = Partial<Person> /*PersonOption = interface { name?: string, age?: number }*/ // Partial 的源码 type Partial<T> = { [P in keyof T]?: T[P]; // ? 表示可选 };
extends
在类型别名
WeakValidationMap
的赋值处,出现了extends
操作符type WeakValidationMap<T> = { [K in keyof T]?: null extends T[K] ? Validator<T[K] | null | undefined> : undefined extends T[K] ? Validator<T[K] | null | undefined> : Validator<T[K]> };
extends
作用是判断一个类型是否满足另一个类型的约束。进行泛型约束
interface IWithLength { length: number } function echoWithArr<T extends IWithLength>(arg: T): T { console.log(arg.length) // 将来传入的参数中,不一定有length。于是需要 extends 进行约束:传入的之中,必须有 length 属性 return arg; }
条件类型关键字
WeakValidationMap
中extends
的作用便是条件类型关键字,产生一个条件类型。type NonType<T> = T extends null | undefined ? never : T // 假如泛型参数 T 为 null 或 undefined, 返回 never;否则返回 T // NonType<T> 变为条件类型:是什么类型,看传入的泛型 T 的类型。 let demo1: NonType<number> // demo1 的类型是 number let demo1: NonType<null> // demo1 的类型是 never
Partial
的实现
keyof
操作符,获取键值in
操作符,用作循环
interface CountryResp {
name: string;
area: number;
population: number;
}
// keyof
type keys = keyof CountryResp // keys = 'name' | 'area' | 'population'
// 在 keys 中的取值
type NameType = CountryResp['name']
type CountryOpt = {
[p in Keys]?: CountryResp[p]
}
// CountryOpt = {
// name?: string;
// area?: number;
// population?: number;
// }
// Partial 的源码
type Partial<T> = {
[P in keyof T]?: T[P];
};
常量类型
上面注释中的代码
keys = 'name' | 'area' | 'population'
'name'
'area'
'population'
就是常量类型注意下面的代码
const str = '123' // 用const定义常量, str 类型为 '123' 类型 let str1 = '123' // str1 类型是 string
类型断言
使用 as
操作符实现
function getLength(input: number | string) { // 使用了联合类型(ps:一般不要在 '{' 后面写注释,这里只是方便讲解)
const str = input as string // 视为 string 类型
// 通过某个类型特有的属性,判断断言是否成立
if (str.length) {
return str.length
} else {
const number = input as number //
return number.toString().length
}
}
注意,类型断言不是类型转换,如果 as
后面是一个新的类型,将报错
// 我们将上面的代码中的任何一个 as 后面的类型换为未指定类型
const number = input as boolean // 报错:类型 "string | number" 到类型 "boolean" 的转换可能是错误的...
定义文件
用于 ts
编译时的检查,没有实现真正的代码功能
基础
定义文件命名:
xx.d.ts
使用
declare
declare var JQuery: (selector: string) => any;
如果 JQuery 是通过
<script>
标签引入,不是通过import
引入,则以上声明可以使ts
不报错npm 包名为 @types/xx 是声明文件
@type/XX
包的创建,需要向 DefinitelyTyped 提交定义文件,需要其审批。社区默认情况下,node_modules 下面的 @types 包都会被编译器自动加载
高级用法
编写声明文件
type HTTPMethod = 'GET' | 'POST' | 'PATCH' |'DELETE'
declare function myFetch<T = any>(url: string, method: HTTPMethod, data?: any): Promise<T>
declare namespace myFetch {
const get: <T = any>(url: string) => Promise<T>;
const post: <T = any>(url: string, data: any) => Promist<T>;
}
export = myFetch // 放入 node_modules 后要添加
可以放入一下文件夹中
node_modules
|—— @tayps
|—— myFetch
|—— index.d.ts
现在,在 ts
文件中写 myFatch
方法时,就可一个获得提示了
注意:
- 这样做,只是为了在书写
ts
代码时获得良好的提示。实际方法的执行逻辑是没有的,在编译成js
执行会报错 import
导入的文件,实际上是node_modules/@types/myFetch/index
下面看看,真正使用环境下的目录
注意:
ts
声明文件的文件名,应和实际的js
文件名保持一致(.d.ts是声明文件的固定格式)
知识点总结
- 基本类型
- 类型推断
- interface
- class
- 泛型
react
FunctionComponent
定义文件源码- 类型别名
- 联合类型
- 交叉类型
- Partial
- keyof, in
- 常量类型
- extends
- 类型断言
- 编写声明文件
内置类型还包括很多
- Promise ...
学习方法
多看别人的定义文件,尤其是大项目的。